package com.hframe.param;

import com.hframe.data.Base;
import com.hframe.data.DRef;
import com.hframe.type.*;
import com.hframe.typefilter.*;
import org.apache.log4j.Logger;

import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Map;

/**
 * 网络提示的参数，转为数据对像
 * Created by heqian on 17-5-18.
 */
public class ParamToData<T extends Base> implements FilterRef<T, Object> {
    private static Logger log = Logger.getLogger(ParamToData.class);

    @Override
    public T filter(Object object) throws Exception {
        return null;
    }

    public static boolean convert(Map<String, String[]> params, Base data, String... keys) throws Exception {
        if (null == params || null == data)
            return false;

        if (data instanceof DRef) {
            for (Map.Entry<String, String[]> item : params.entrySet()) {
                String[] values = item.getValue();
                if (null != values && 0 <= values.length)
                    convert(values[0], (DRef<?>) data, item.getKey(), keys);
            }
            return true;

        } else if (data instanceof TData) {
            for (Map.Entry<String, String[]> item : params.entrySet()) {
                String[] values = item.getValue();
                if (null != values && 0 <= values.length)
                    convert(values[0], (DRef<?>) ((TData) data).get(), item.getKey(), keys);
            }
            return true;

        } else if (data instanceof TArray) {
            return convertArray(params, (TArray) data, keys);

        }
        return false;

    }

    private static boolean convertArray(Map<String, String[]> params, TArray data, String... keys) {
        if (!isExtendsClass(data.getClassName(), DRef.class))
            return false;

        for (Map.Entry<String, String[]> item : params.entrySet()) {
            String[] values = item.getValue();
            if (null == values)
                continue;

            for (int i = 0; i < values.length; i++) {
                DRef<?> object = (DRef<?>) data.get(i);
                if (null == object) {
                    try {
                        object = (DRef<?>) data.getClassName().newInstance();
                    } catch (Exception e) {
                        log.error(e);
                        return false;
                    }
                    data.add(object);
                }
                convert(values[i], object, item.getKey(), keys);
            }
        }
        return true;
    }

    private static boolean convert(String value, DRef<?> data, String key, String[] keys) {
        if (null == data)
            return false;

        try {
            com.hframe.type.T<?> object = findField(data, key, keys);
            if (null != object) {
                return setDataValue(object, value);
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return false;
        }
        return true;
    }


    protected static com.hframe.type.T<?> findField(DRef<?> data, String key, String... keys) {
        if (null == key || 0 >= key.trim().length())
            return null;

        Object object;
        Class<?> cla = data.getClass();
        while (null != cla && DRef.class != cla) {
            for (Field field : cla.getDeclaredFields()) {
                field.setAccessible(true);
                try {
                    if (!isExtendsClass(field.getType(), com.hframe.type.T.class))
                        continue;

                    String fieldName = field.getName();
                    if (keys.length > 0 && !isOut(fieldName, keys))
                        return null;

                    if (null == (object = field.get(data)))
                        continue;

                    com.hframe.type.T<?> typeObj = (com.hframe.type.T<?>) object;
                    ParamFiletr filter = typeObj.getfilter(ParamFiletr.class);
                    if (null != filter) {
                        if (filter.getReadWait() == ParamFiletr.ReadWait.OnlyRead || filter.getReadWait() == ParamFiletr.ReadWait.NotReadWait)
                            return null;

                        fieldName = filter.getName();
                    }

                    if (null == fieldName)
                        fieldName = field.getName();

                    if (key.toLowerCase().equals(fieldName.toLowerCase())) {
                        return typeObj;
                    }

                } catch (Exception e) {
                    log.error(e.getMessage(), e);
                }
            }
            cla = cla.getSuperclass();
        }
        return null;
    }

    private static boolean isOut(String name, String... keys) {
        for (String key : keys) {
            if (key.toLowerCase().equals(name.toLowerCase()))
                return true;
        }
        return false;
    }

    protected static boolean setDataValue(com.hframe.type.T<?> typeObj, String val) throws Exception {
        if (null == val || null == typeObj)
            return false;

        ParamFiletr filter = typeObj.getfilter(ParamFiletr.class);
        if (null != filter) {
            filter.set(val.toString());
        } else {
            if (typeObj instanceof TShort) {
                ((TShort) typeObj).set(StringToShort.parse(val));

            } else if (typeObj instanceof TLong) {
                ((TLong) typeObj).set(StringToLong.parse(val));

            } else if (typeObj instanceof TInt) {
                ((TInt) typeObj).set(StringToInteger.parse(val));

            } else if (typeObj instanceof TFloat) {
                ((TFloat) typeObj).set(StringToFloat.parse(val));

            } else if (typeObj instanceof TDouble) {
                ((TDouble) typeObj).set(StringToDouble.parse(val));

            } else if (typeObj instanceof TChar) {
                ((TChar) typeObj).set(StringToCharacter.parse(val));

            } else if (typeObj instanceof TByte) {
                ((TByte) typeObj).set(StringToByte.parse(val));

            } else if (typeObj instanceof TBoolean) {
                ((TBoolean) typeObj).set(StringToBoolean.parse(val));

            } else if (typeObj instanceof TDate) {
                ((TDate) typeObj).set(StringToDate.parse(val, StringToDate.DATE_TIME_LINE));

            } else if (typeObj instanceof TString) {
                ((TString) typeObj).set(val);

            } else if (typeObj instanceof TBigInteger) {
                if (0 < val.trim().length())
                    ((TBigInteger) typeObj).set(new BigInteger(val.trim()));

            } else if (typeObj instanceof TBigDecimal) {
                if (0 < val.trim().length())
                    ((TBigDecimal) typeObj).set(new BigDecimal(val.trim()));

            } else if (typeObj instanceof TEnum) {
                ((TEnum) typeObj).set(getEnumValue(typeObj, StringToInteger.parse(val)));

            } else {
                return false;
            }
        }
        return true;
    }

    private static Enum getEnumValue(com.hframe.type.T<?> typeObj, Integer val) throws IllegalAccessException {
        if (null == val)
            return null;

        Class<Enum> className = (Class<Enum>) typeObj.getClassName();
        if (null != className) {
            Field[] fields = className.getDeclaredFields();
            for (Field field : fields) {
                if (field.isEnumConstant()) {

                    Object object = field.get(null);
                    if (((Enum) object).ordinal() == val) {
                        return (Enum) object;
                    }
                }
            }
        }
        return null;
    }

    protected static boolean isExtendsClass(Class<?> src, Class<?> dis) {
        while (src != null) {
            if (src == dis)
                return true;
            src = src.getSuperclass();
        }
        return false;
    }
}
